package io.github.wslxm.springbootplus2.gateway.aopfilter;

import io.github.wslxm.springbootplus2.constant.CacheKey;
import io.github.wslxm.springbootplus2.core.base.annotation.LimitScope;
import io.github.wslxm.springbootplus2.core.base.annotation.XjCurrentLimit;
import io.github.wslxm.springbootplus2.core.base.annotation.XjCurrentLimitAll;
import io.github.wslxm.springbootplus2.core.constant.SymbolConst;
import io.github.wslxm.springbootplus2.core.result.Result;
import io.github.wslxm.springbootplus2.core.utils.XjIpUtil;
import io.github.wslxm.springbootplus2.starter.redis.constant.CacheSeparateConst;
import io.github.wslxm.springbootplus2.starter.redis.util.RedisUtil;
import io.github.wslxm.springbootplus2.starter.redis.util.SpelUtil;
import io.github.wslxm.springbootplus2.utils.JwtUtil;
import io.github.wslxm.springbootplus2.utils.XjAuthCacheKeyUtil;
import io.github.wslxm.springbootplus2.utils.XjLimitUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;

/**
 * 接口限流 （基于 com.google.guava 的令牌桶）
 *
 * @author wangsong
 * @version 1.0.0
 * @email 1720696548@qq.com
 * @date 2021/11/28 0028 9:49
 */
@Slf4j
@Component
public class RateLimiterFilter {


    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private HttpServletRequest request;

    /**
     * 限流， 接口服务超出直接提示 访问人数过多
     *
     * @author wangsong
     * @mail 1720696548@qq.com
     * @date 2021/11/28 0028 11:28
     * @version 1.0.0
     */
    public Result run(ProceedingJoinPoint proceed,String methodUri) {
        // 获取请求参数
        MethodSignature signature = (MethodSignature) proceed.getSignature();
        XjCurrentLimit xjCurrentLimit = signature.getMethod().getAnnotation(XjCurrentLimit.class);
        XjCurrentLimitAll xjCurrentLimitAll = signature.getMethod().getAnnotation(XjCurrentLimitAll.class);
        if (xjCurrentLimit == null && xjCurrentLimitAll == null) {
            return Result.success();
        }
        // 获取请求uri, 充当限流 name (name = cacheKey）
        String uriCacheKey = XjAuthCacheKeyUtil.get(request.getMethod(), methodUri);
        String cachePrefix = redisUtil.getPrefix() + CacheKey.LIMITS + CacheSeparateConst.SEPARATE + uriCacheKey + CacheSeparateConst.SEPARATE;
        if (xjCurrentLimit != null) {
            this.currentLimitHandler(proceed, xjCurrentLimit, cachePrefix);
        }
        if (xjCurrentLimitAll != null) {
            for (XjCurrentLimit rule : xjCurrentLimitAll.value()) {
                this.currentLimitHandler(proceed, rule, cachePrefix);
            }
        }
        return Result.success();
    }


    /**
     * 限流处理
     *
     * @author 王松
     * @mail 1720696548@qq.com
     * @date 2025/5/23 10:39
     */
    private void currentLimitHandler(ProceedingJoinPoint joinPoint, XjCurrentLimit xjCurrentLimit, String cachePrefix) {
        String key = cachePrefix + xjCurrentLimit.timeUnit().name() + SymbolConst.IN_THELINE + xjCurrentLimit.scope();
        if (xjCurrentLimit.scope().equals(LimitScope.GLOBAL)) {
            //
        } else if (xjCurrentLimit.scope().equals(LimitScope.IP)) {
            key += SymbolConst.IN_THELINE + XjIpUtil.getIp(request);
        } else if (xjCurrentLimit.scope().equals(LimitScope.USER)) {
            key += SymbolConst.IN_THELINE + JwtUtil.getJwtUser(request).getUserId();
        } else if (xjCurrentLimit.scope().equals(LimitScope.CUSTOM)) {
            Method method = this.getSpecificMethod(joinPoint);
            Object parse = SpelUtil.parse(joinPoint.getTarget(), xjCurrentLimit.limitKey(), method, joinPoint.getArgs());
            String paramKey = parse != null ? parse.toString() : "";
            key += SymbolConst.IN_THELINE + paramKey;
        }
        // 增加限流
        XjLimitUtil.limit(key,xjCurrentLimit.qps(),xjCurrentLimit.timeUnit(), xjCurrentLimit.errorMsg());
    }


    private Method getSpecificMethod(ProceedingJoinPoint pjp) {
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method method = methodSignature.getMethod();
        // The method may be on an interface, but we need attributes from the
        // target class. If the target class is null, the method will be
        // unchanged.
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(pjp.getTarget());
        if (targetClass == null && pjp.getTarget() != null) {
            targetClass = pjp.getTarget().getClass();
        }
        Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
        // If we are dealing with method with generic parameters, find the
        // original method.
        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
        return specificMethod;
    }
}
